import os

class Config:
    """静态配置类，保存文件路径"""
    fix_tlef = r"sky130_fd_sc_hs.tlef"              # 输入TLEF文件路径
    fix_def = r"iTO_fix_fanout_result.def"          # 输入DEF文件路径
    temp_def = r"tem_iTO_fix_fanout_fix.def"        # 临时文件路径（用于存储处理完COMPONENTS的中间结果）
    output_def = r"iTO_fix_fanout_fix.def"          # 最终输出文件路径
    lef_files = [                                   # 需要合并的lef文件
        r"sky130_ef_io__com_bus_slice_1um.lef",
        r"sky130_ef_io__com_bus_slice_5um.lef",
        r"sky130_ef_io__com_bus_slice_10um.lef",
        r"sky130_ef_io__com_bus_slice_20um.lef",
        r"sky130_ef_io__connect_vcchib_vccd_and_vswitch_vddio_slice_20um.lef",
        r"sky130_ef_io__corner_pad.lef",
        r"sky130_ef_io__disconnect_vccd_slice_5um.lef",
        r"sky130_ef_io__disconnect_vdda_slice_5um.lef"
    ]


def parse_tlef(tlef_path):
    """
    解析TLEF文件，获取各层的主方向（DIRECTION）
    返回格式: {层名: 方向(VERTICAL/HORIZONTAL)}
    """
    layer_directions = {}
    current_layer = None

    with open(tlef_path, 'r', encoding='utf-8', errors='ignore') as f:
        for line in f:
            line = line.strip()
            # 检测层定义开始
            if line.startswith('LAYER'):
                parts = line.split()
                if len(parts) >= 2:
                    current_layer = parts[1]
            # 检测方向定义（在当前层内）
            elif current_layer and 'DIRECTION' in line:
                parts = line.split()
                if len(parts) >= 2:
                    direction = parts[1].strip(';').upper()  # 去除分号并转为大写
                    layer_directions[current_layer] = direction
                    current_layer = None  # 重置当前层，避免重复解析

    return layer_directions


def parse_def_tracks(def_path):
    """
    解析DEF文件中的TRACKS信息
    返回格式: {层名: {X: 轨道信息列表, Y: 轨道信息列表}}
    """
    tracks = {}
    with open(def_path, 'r', encoding='utf-8', errors='ignore') as f:
        for line in f:
            line = line.strip()
            # 检测TRACKS行（忽略大小写，确保关键字匹配）
            if line.upper().startswith('TRACKS'):
                # 分割行，保留原始顺序，过滤空字符串
                parts = [p for p in line.split() if p]
                if not parts:
                    continue  # 空行跳过

                # 提取方向（X/Y，第2个字段，如 TRACKS X ... → 方向是X）
                if len(parts) >= 2 and parts[1].upper() in ['X', 'Y']:
                    direction = parts[1].upper()
                else:
                    continue  # 无有效方向，跳过

                # 查找"LAYER"关键字的位置，提取层名
                layer = None
                for i in range(len(parts)):
                    if parts[i].upper() == 'LAYER' and i + 1 < len(parts):
                        # 层名可能带分号（如 li1;），需要清理
                        layer = parts[i + 1].strip(';').strip()
                        break
                if not layer:
                    continue  # 未找到有效层名，跳过

                # 保存完整轨道信息（原始行）
                track_info = line

                # 初始化并添加到轨道字典
                if layer not in tracks:
                    tracks[layer] = {'X': [], 'Y': []}
                tracks[layer][direction].append(track_info)

    return tracks


def reconstruct_def_with_fixed_tracks(original_def_path, output_def_path, layer_directions, fixed_tracks):
    """
    重构DEF文件，彻底替换TRACKS区域为修正后的内容，避免重复
    """
    with open(original_def_path, 'r', encoding='utf-8', errors='ignore') as f_in, \
            open(output_def_path, 'w', encoding='utf-8') as f_out:

        in_tracks_section = False  # 是否处于TRACKS区域
        tracks_written = False     # 修正后的TRACKS是否已写入

        for line in f_in:
            stripped_line = line.strip()

            # 检测TRACKS区域开始
            if not in_tracks_section and stripped_line.upper().startswith('TRACKS'):
                in_tracks_section = True
                # 尚未写入修正后的TRACKS时，一次性写入所有层的修正内容
                if not tracks_written:
                    # 按层输出修正后的TRACKS（保持原始顺序）
                    for layer in fixed_tracks:
                        for track_line in fixed_tracks[layer]:
                            f_out.write(track_line + '\n')
                    tracks_written = True  # 标记为已写入，避免重复
                continue  # 跳过原始TRACKS行

            # 检测TRACKS区域结束
            if in_tracks_section and stripped_line and stripped_line[0].isupper() and not stripped_line.upper().startswith('TRACKS'):
                in_tracks_section = False  # 退出TRACKS区域

            # 非TRACKS区域的内容正常写入
            if not in_tracks_section:
                f_out.write(line)


def add_unplaced_to_components(temp_file_path, output_file_path):
    """
    读取临时DEF文件，给COMPONENTS区域中未放置的单元添加 + UNPLACED 后缀
    """
    with open(temp_file_path, 'r', encoding='utf-8', errors='ignore') as f_in, \
         open(output_file_path, 'w', encoding='utf-8') as f_out:

        in_components = False  # 是否处于COMPONENTS区域
        prev_line = ""  # 用于缓存上一行（处理单元行与分号行分离的情况）

        for line in f_in:
            stripped_line = line.strip()
            current_line = line

            # 检测COMPONENTS区域开始
            if stripped_line.startswith('COMPONENTS'):
                in_components = True
                f_out.write(current_line)
                prev_line = current_line
                continue

            # 检测COMPONENTS区域结束
            if in_components and stripped_line == 'END COMPONENTS':
                in_components = False
                f_out.write(current_line)
                prev_line = current_line
                continue

            # 处理COMPONENTS区域内的内容
            if in_components:
                # 识别未放置单元行（以"-"开头，且下一行是分号）
                if prev_line.lstrip().startswith('-') and stripped_line == ';':
                    # 提取单元行内容（去除末尾换行）
                    unit_line = prev_line.rstrip('\n')
                    if('FIXED' not in unit_line):
                        print("提取到的单元行：\n" + unit_line)

                        # 给单元行添加 + UNPLACED
                        modified_unit_line = f"{unit_line} + UNPLACED\n"
                        # 写入修改后的单元行和分号行
                        f_out.write(modified_unit_line)
                        f_out.write(current_line)
                        # 清空prev_line，避免重复处理
                        prev_line = ""
                        continue
                else:
                    # 非目标单元行，直接写入并更新prev_line
                    if prev_line:
                        f_out.write(prev_line)
                    prev_line = current_line
            else:
                # 非COMPONENTS区域，直接写入
                f_out.write(current_line)

        # 写入最后一行缓存（如果有）
        if prev_line:
            f_out.write(prev_line)

    print(f"已为未放置单元添加 + UNPLACED，输出文件：{output_file_path}")


def fix_def(def_path, tlef_path):
    """
    主函数：修正DEF文件中的TRACKS顺序，确保主方向轨道信息在后
    输出文件：iTO_fix_fanout_fix.def
    """
    # 1. 解析TLEF获取各层主方向
    layer_directions = parse_tlef(tlef_path)
    print(f"解析到的层方向: {layer_directions}")

    # 2. 解析DEF中的TRACKS信息
    tracks = parse_def_tracks(def_path)
    print(f"解析到的轨道信息: {tracks}")

    # 3. 修正TRACKS顺序（主方向在后）
    fixed_tracks = {}
    for layer, dir_tracks in tracks.items():
        # 获取该层的主方向（默认VERTICAL）
        main_direction = layer_directions.get(layer, 'VERTICAL')
        # 主方向对应的轨道方向（VERTICAL对应X轨道，HORIZONTAL对应Y轨道）
        main_track_dir = 'X' if main_direction == 'VERTICAL' else 'Y'
        other_track_dir = 'Y' if main_track_dir == 'X' else 'X'

        # 合并轨道信息（非主方向在前，主方向在后）
        fixed_order = []
        # 添加非主方向轨道（去重，保留最后一个）
        if dir_tracks[other_track_dir]:
            fixed_order.append(dir_tracks[other_track_dir][-1])  # 取最后一个
        # 添加主方向轨道（去重，保留最后一个）
        if dir_tracks[main_track_dir]:
            fixed_order.append(dir_tracks[main_track_dir][-1])  # 取最后一个

        fixed_tracks[layer] = fixed_order

        # 生成临时文件（含修正后的TRACKS）
        reconstruct_def_with_fixed_tracks(def_path, Config.temp_def, layer_directions, fixed_tracks)
        print(f"TRACKS处理完成，临时文件：{Config.temp_def}")

        # 4. 处理临时文件，添加UNPLACED后缀
        add_unplaced_to_components(Config.temp_def, Config.output_def)


def combine_lef(*lefs, output="merged.lef"):
    """
    合并多个LEF文件为一个merged.lef
    :param lefs: 可变参数，传入LEF文件路径列表
    :param output: 输出合并后的文件名，默认"merged.lef"
    """
    if not lefs:
        raise ValueError("至少需要传入一个LEF文件")

    # 读取第一个文件的头部（保留VERSION等定义）和其MACRO内容
    header = []
    first_macro = []
    with open(lefs[0], 'r', encoding='utf-8') as f:
        in_header = True  # 是否在头部区域
        in_macro = False  # 是否在MACRO块内部
        for line in f:
            stripped = line.strip()
            if stripped.startswith("#"):
                continue  # 跳过注释
            if in_header:
                # 头部截止到第一个MACRO定义前
                if stripped.startswith("MACRO"):
                    in_header = False
                    in_macro = True
                    first_macro.append(stripped + "\n")
                else:
                    if stripped:
                        header.append(stripped + "\n")
            else:
                # 收集第一个文件的MACRO内容
                if stripped.startswith("END LIBRARY"):
                    in_macro = False
                elif in_macro:
                    if stripped:
                        first_macro.append(stripped + "\n")

    # 处理中间文件（除第一个和最后一个）：去头去尾、去注释、保留MACRO
    middle_content = []
    # 中间文件范围：从索引1到索引-2（包含）
    for file in lefs[1:-1] if len(lefs) > 2 else []:
        with open(file, 'r', encoding='utf-8') as f:
            in_macro = False
            for line in f:
                stripped = line.strip()
                if stripped.startswith("#"):
                    continue
                if stripped.startswith("MACRO"):
                    in_macro = True
                    middle_content.append(stripped + "\n")
                elif stripped.startswith("END LIBRARY"):
                    in_macro = False
                elif in_macro:
                    if stripped:
                        middle_content.append(stripped + "\n")

    # 处理最后一个文件：保留MACRO内容和END LIBRARY
    last_content = []
    with open(lefs[-1], 'r', encoding='utf-8') as f:
        in_macro = False
        for line in f:
            stripped = line.strip()
            if stripped.startswith("#"):
                continue
            if stripped.startswith("MACRO"):
                in_macro = True
                last_content.append(stripped + "\n")
            elif stripped.startswith("END LIBRARY"):
                last_content.append(stripped + "\n")
                break
            elif in_macro:
                if stripped:
                    last_content.append(stripped + "\n")

    # 合并所有内容（头部 + 第一个文件的MACRO + 中间文件 + 最后一个文件）
    with open(output, 'w', encoding='utf-8') as f:
        f.writelines(header)           # 头部
        f.writelines(first_macro)      # 第一个文件的MACRO内容（之前漏掉的）
        f.writelines(middle_content)   # 中间文件
        f.writelines(last_content)     # 最后一个文件

    print(f"合并完成，输出文件：{os.path.abspath(output)}")


if __name__ == "__main__":
    # 修正def文件
    fix_def(Config.fix_def, Config.fix_tlef)

    # 合并lef文件
    combine_lef(*Config.lef_files)